#ifndef __ATOM_H_VERSION_KL__
#define __ATOM_H_VERSION_KL__

#include "thread.h"

KL_BEGIN
// warning: make sure you call push/pop in different threads.
template<class Type, class Container = KL::deque<Type> >
class AtomQue
{
public:
	typedef Container container_type;
	typedef typename Container::value_type value_type;
protected:
	container_type _c;
	size_t _empty_size;
	size_t _full_size;
	KL::ConditionVariable _full_cond;
	KL::ConditionVariable _empty_cond;
	KL::Mutex _mutex;
	static const i4_t SleepTime = 3000; // 3 seconds.
	AtomQue(AtomQue const &); // no copy.
	void operator = (AtomQue const &);
public:
	AtomQue(size_t empty_size = 0, size_t ful_size = KL::Value::Npos)
		: _full_size(ful_size), _empty_size(empty_size)
	{
		if(_full_size <= _empty_size)
		{
			if(_full_size) _empty_size = _full_size - 1;
		}
	}
	bool pop(value_type &out)
	{
		MutexLocker lo_(&_mutex);
		if(_c.size() > _empty_size)
		{
			out = _c.front();
			_c.pop_front();
			_full_cond.awake();
			return true;
		}
		return false;
	}
	bool pop_wait(value_type &out)
	{
		while(_c.size() <= _empty_size) _empty_cond.sleep(SleepTime);
		return pop(out);
	}
	bool push(value_type const &value)
	{
		MutexLocker lo_(&_mutex);
		if(_c.size() < _full_size)
		{
			_c.push_back(value);
			_empty_cond.awake();
			return true;
		}
		return false;
	}
	bool push_wait(value_type const &value)
	{
		while(_c.size() >= _full_size) _full_cond.sleep(SleepTime);
		return push(value);
	}
	size_t size() const
	{
		return _c.size();
	}
	void reset_full(size_t size)
	{	// reset full size, empty size will be at most full size - 1.
		if(size == 0) return;
		MutexLocker lo_(&Mutex);
		_full_size = size;
		if(_empty_size >= _full_size) _empty_size = _full_size - 1;
	}
	void reset_empty(size_t size)
	{	// reset empty size, full size will be at least empty size + 1.
		MutexLocker lo_(&Mutex);
		_empty_size = size;
		if(_empty_size >= _full_size) _full_size = _empty_size + 1;
	}
};
// warning: make sure you call push/pop in different threads.
template<class Type, class Container>
class AtomStack
{
public:
	typedef Container container_type;
	typedef typename Container::value_type value_type;
protected:
	container_type _c;
	size_t _empty_size;
	size_t _full_size;
	KL::ConditionVariable _full_cond;
	KL::ConditionVariable _empty_cond;
	KL::Mutex _mutex;
	static const i4_t SleepTime = 3000; // 3 seconds.
	AtomStack(AtomStack const &); // no copy.
	void operator = (AtomStack const &);
public:
	AtomStack(size_t empty_size = 0, size_t ful_size = KL::Value::Npos)
		: _full_size(ful_size), _empty_size(empty_size)
	{
		if(_full_size <= _empty_size)
		{
			if(_full_size) _empty_size = _full_size - 1;
		}
	}
	bool pop(value_type &out)
	{
		MutexLocker lo_(&Mutex);
		if(_c.size() > _empty_size)
		{
			out = _c.back();
			_c.pop_back();
			_full_cond.awake();
			return true;
		}
		return false;
	}
	bool pop_wait(value_type &out)
	{
		while(_c.size() <= _empty_size) _empty_cond.sleep(SleepTime);
		return pop(out);
	}
	bool push(value_type const &value)
	{
		MutexLocker lo_(&Mutex);
		if(_c.size() < _full_size)
		{
			_c.push_back(value);
			_empty_cond.awake();
			return true;
		}
		return false;
	}
	bool push_wait(value_type const &value)
	{
		while(_c.size() >= _full_size) _full_cond.sleep(SLEEP_TIME);
		return push(value);
	}
	size_t size() const
	{
		return _c.size();
	}
	void reset_full(size_t size)
	{	// reset full size, empty size will be at most full size - 1.
		if(size == 0) return;
		MutexLocker lo_(&Mutex);
		_full_size = size;
		if(_empty_size >= _full_size) _empty_size = _full_size - 1;
	}
	void reset_empty(size_t size)
	{	// reset empty size, full size will be at least empty size + 1.
		MutexLocker lo_(&Mutex);
		_empty_size = size;
		if(_empty_size >= _full_size) _full_size = _empty_size + 1;
	}
};
KL_END
#endif // __ATOM_H_VERSION_KL__
